package com.ruoyi.tzai.controller;

import java.util.List;
import java.io.OutputStream;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.tzai.domain.FileInfo;
import com.ruoyi.tzai.service.IFileInfoService;
import com.ruoyi.tzai.service.IFileUploadService;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.utils.StringUtils;
import com.alibaba.fastjson2.JSON;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 文件管理Controller
 * 
 * @author tzai
 * @date 2025-11-03
 */
@Api(tags = "文件管理")
@RestController
@RequestMapping("/tzai/file")
public class FileManageController extends BaseController
{
    private static final Logger log = LoggerFactory.getLogger(FileManageController.class);
    
    @Autowired
    private IFileInfoService fileInfoService;
    
    @Autowired
    private IFileUploadService fileUploadService;

    /**
     * 查询文件信息列表
     */
    @ApiOperation("查询文件信息列表")
    @PreAuthorize("@ss.hasPermi('tzai:file:list')")
    @GetMapping("/list")
    public TableDataInfo list(FileInfo fileInfo)
    {
        startPage();
        List<FileInfo> list = fileInfoService.selectFileInfoList(fileInfo);
        return getDataTable(list);
    }

    /**
     * 导出文件信息列表
     */
    @ApiOperation("导出文件信息列表")
    @PreAuthorize("@ss.hasPermi('tzai:file:export')")
    @Log(title = "文件信息", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    public void export(HttpServletResponse response, FileInfo fileInfo)
    {
        List<FileInfo> list = fileInfoService.selectFileInfoList(fileInfo);
        ExcelUtil<FileInfo> util = new ExcelUtil<FileInfo>(FileInfo.class);
        util.exportExcel(response, list, "文件信息数据");
    }

    /**
     * 获取文件信息详细信息
     */
    @ApiOperation("获取文件信息详细信息")
    @PreAuthorize("@ss.hasPermi('tzai:file:query')")
    @GetMapping(value = "/{fileId}")
    public AjaxResult getInfo(@PathVariable("fileId") Long fileId)
    {
        return success(fileInfoService.selectFileInfoByFileId(fileId));
    }

    /**
     * 根据业务类型和业务ID查询文件
     */
    @ApiOperation("根据业务类型和业务ID查询文件")
    @GetMapping("/biz")
    public AjaxResult getByBiz(@RequestParam String bizType, @RequestParam Long bizId)
    {
        FileInfo query = new FileInfo();
        query.setBizType(bizType);
        query.setBizId(bizId);
        query.setStatus("1"); // 只查询正常状态
        List<FileInfo> list = fileInfoService.selectFileInfoList(query);
        
        // 返回第一个（最新的）
        if (list != null && !list.isEmpty()) {
            return success(list.get(0));
        }
        return success(null);
    }

    /**
     * 新增文件信息
     */
    @ApiOperation("新增文件信息")
    @PreAuthorize("@ss.hasPermi('tzai:file:add')")
    @Log(title = "文件信息", businessType = BusinessType.INSERT)
    @PostMapping
    public AjaxResult add(@RequestBody FileInfo fileInfo)
    {
        return toAjax(fileInfoService.insertFileInfo(fileInfo));
    }

    /**
     * 修改文件信息
     */
    @ApiOperation("修改文件信息")
    @PreAuthorize("@ss.hasPermi('tzai:file:edit')")
    @Log(title = "文件信息", businessType = BusinessType.UPDATE)
    @PutMapping
    public AjaxResult edit(@RequestBody FileInfo fileInfo)
    {
        return toAjax(fileInfoService.updateFileInfo(fileInfo));
    }

    /**
     * 删除文件信息
     */
    @ApiOperation("删除文件信息")
    @PreAuthorize("@ss.hasPermi('tzai:file:remove')")
    @Log(title = "文件信息", businessType = BusinessType.DELETE)
	@DeleteMapping("/{fileIds}")
    public AjaxResult remove(@PathVariable Long[] fileIds)
    {
        // TODO: 同时删除OSS上的文件
        return toAjax(fileInfoService.deleteFileInfoByFileIds(fileIds));
    }
    
    /**
     * 上传文件
     * 使用 x-file-storage 上传到阿里云OSS
     */
    @ApiOperation("上传文件")
    @PreAuthorize("@ss.hasPermi('tzai:file:upload')")
    @Log(title = "文件上传", businessType = BusinessType.INSERT)
    @PostMapping("/upload")
    public AjaxResult upload(@RequestParam("file") MultipartFile file,
                            @RequestParam(required = false) String bizType,
                            @RequestParam(required = false) Long bizId)
    {
        try {
            log.info("上传文件: {}, 业务类型: {}, 业务ID: {}", file.getOriginalFilename(), bizType, bizId);
            
            // 1. 确定文件类型
            String contentType = file.getContentType();
            String fileType = determineFileType(contentType);
            
            // 2. 使用 x-file-storage 上传文件
            org.dromara.x.file.storage.core.FileInfo uploadResult = fileUploadService.uploadFile(file, fileType);
            if (uploadResult == null) {
                return error("文件上传失败");
            }
            
            log.info("文件上传成功: {}, URL: {}", uploadResult.getFilename(), uploadResult.getUrl());
            
            // 3. 如果有bizType和bizId，先将旧文件设置为停用状态
            if (StringUtils.isNotEmpty(bizType) && bizId != null) {
                FileInfo queryOld = new FileInfo();
                queryOld.setBizType(bizType);
                queryOld.setBizId(bizId);
                queryOld.setStatus("1"); // 查询正常状态的旧文件
                List<FileInfo> oldFiles = fileInfoService.selectFileInfoList(queryOld);
                
                if (oldFiles != null && !oldFiles.isEmpty()) {
                    log.info("发现{}个旧文件，将其状态设置为停用", oldFiles.size());
                    for (FileInfo oldFile : oldFiles) {
                        oldFile.setStatus("0"); // 停用
                        oldFile.setUpdateBy(getUsername());
                        fileInfoService.updateFileInfo(oldFile);
                    }
                }
            }
            
            // 4. 保存新文件信息到数据库
            FileInfo fileInfo = new FileInfo();
            fileInfo.setFileName(uploadResult.getFilename());
            fileInfo.setOriginalName(file.getOriginalFilename());
            fileInfo.setFilePath(uploadResult.getPath());
            fileInfo.setFileUrl(uploadResult.getUrl());
            fileInfo.setFileType(fileType);
            fileInfo.setFileSize(file.getSize());
            fileInfo.setContentType(contentType);
            fileInfo.setStoragePlatform(uploadResult.getPlatform());
            fileInfo.setFileMetadata(JSON.toJSONString(uploadResult));
            fileInfo.setBizType(bizType);
            fileInfo.setBizId(bizId);
            fileInfo.setStatus("1");
            fileInfo.setCreateBy(getUsername());
            
            fileInfoService.insertFileInfo(fileInfo);
            
            log.info("文件信息保存成功: fileId={}", fileInfo.getFileId());
            
            // 5. 返回文件信息（包含fileId和URL）
            return success(fileInfo);
            
        } catch (Exception e) {
            log.error("文件上传失败: " + file.getOriginalFilename(), e);
            return error("文件上传失败: " + e.getMessage());
        }
    }
    
    /**
     * 在线查看文件（图片、文档等）
     * 参考音频播放机制，使用 x-file-storage 下载文件并流式传输
     */
    @ApiOperation("在线查看文件")
    @GetMapping("/view/{fileId}")
    public void view(@PathVariable("fileId") Long fileId, HttpServletResponse response)
    {
        try {
            log.info("在线查看文件: {}", fileId);
            
            // 1. 查询文件记录
            FileInfo fileInfo = fileInfoService.selectFileInfoByFileId(fileId);
            if (fileInfo == null) {
                log.error("文件记录不存在: {}", fileId);
                response.setStatus(HttpServletResponse.SC_NOT_FOUND);
                return;
            }
            
            // 2. 解析 x-file-storage 的 FileInfo
            if (fileInfo.getFileMetadata() == null || fileInfo.getFileMetadata().isEmpty()) {
                log.error("文件元数据为空: {}", fileId);
                response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
                return;
            }
            
            org.dromara.x.file.storage.core.FileInfo xFileInfo = JSON.parseObject(
                fileInfo.getFileMetadata(), 
                org.dromara.x.file.storage.core.FileInfo.class
            );
            log.info("解析FileInfo成功: {}", xFileInfo.getFilename());
            
            // 3. 使用 x-file-storage 下载文件
            byte[] fileBytes = fileUploadService.downloadFile(xFileInfo);
            if (fileBytes == null || fileBytes.length == 0) {
                log.error("文件下载失败: {}", fileId);
                response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
                return;
            }
            
            // 4. 设置响应头
            response.setContentType(fileInfo.getContentType());
            response.setHeader("Content-Length", String.valueOf(fileBytes.length));
            response.setHeader("Accept-Ranges", "bytes");
            response.setHeader("Cache-Control", "public, max-age=3600"); // 缓存1小时
            
            // 5. 写入响应流
            OutputStream outputStream = response.getOutputStream();
            outputStream.write(fileBytes);
            outputStream.flush();
            
            log.info("文件查看成功: {} ({}字节)", fileId, fileBytes.length);
            
        } catch (Exception e) {
            log.error("查看文件失败: " + fileId, e);
            try {
                response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            } catch (Exception ex) {
                // ignore
            }
        }
    }
    
    /**
     * 下载文件
     * 使用 x-file-storage 下载文件
     */
    @ApiOperation("下载文件")
    @PreAuthorize("@ss.hasPermi('tzai:file:download')")
    @GetMapping("/download/{fileId}")
    public void download(@PathVariable("fileId") Long fileId, HttpServletResponse response)
    {
        try {
            log.info("下载文件: {}", fileId);
            
            // 1. 查询文件记录
            FileInfo fileInfo = fileInfoService.selectFileInfoByFileId(fileId);
            if (fileInfo == null) {
                log.error("文件记录不存在: {}", fileId);
                response.setStatus(HttpServletResponse.SC_NOT_FOUND);
                return;
            }
            
            // 2. 解析 x-file-storage 的 FileInfo
            if (fileInfo.getFileMetadata() == null || fileInfo.getFileMetadata().isEmpty()) {
                log.error("文件元数据为空: {}", fileId);
                response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
                return;
            }
            
            org.dromara.x.file.storage.core.FileInfo xFileInfo = JSON.parseObject(
                fileInfo.getFileMetadata(), 
                org.dromara.x.file.storage.core.FileInfo.class
            );
            log.info("解析FileInfo成功: {}", xFileInfo.getFilename());
            
            // 3. 使用 x-file-storage 下载文件
            byte[] fileBytes = fileUploadService.downloadFile(xFileInfo);
            if (fileBytes == null || fileBytes.length == 0) {
                log.error("文件下载失败: {}", fileId);
                response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
                return;
            }
            
            // 4. 设置响应头（下载模式）
            response.setContentType("application/octet-stream");
            response.setHeader("Content-Disposition", "attachment; filename=\"" + fileInfo.getOriginalName() + "\"");
            response.setHeader("Content-Length", String.valueOf(fileBytes.length));
            
            // 5. 写入响应流
            OutputStream outputStream = response.getOutputStream();
            outputStream.write(fileBytes);
            outputStream.flush();
            
            log.info("文件下载成功: {} ({}字节)", fileId, fileBytes.length);
            
        } catch (Exception e) {
            log.error("下载文件失败: " + fileId, e);
            try {
                response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            } catch (Exception ex) {
                // ignore
            }
        }
    }
    
    /**
     * 根据ContentType判断文件类型
     */
    private String determineFileType(String contentType)
    {
        if (StringUtils.isEmpty(contentType)) {
            return "other";
        }
        
        contentType = contentType.toLowerCase();
        if (contentType.startsWith("image/")) {
            return "image";
        } else if (contentType.startsWith("audio/")) {
            return "audio";
        } else if (contentType.startsWith("video/")) {
            return "video";
        } else if (contentType.contains("pdf") || contentType.contains("document") || 
                   contentType.contains("word") || contentType.contains("excel") ||
                   contentType.contains("text/")) {
            return "document";
        } else {
            return "other";
        }
    }
}

